#include "testdrawer.h"
#include "../geometry/polygon.h"
#include "../geometry/rayintersections.h"

#include <QtGui/QMouseEvent>
#include <cstdio>
#include <vector>
#include <fstream>
#include <algorithm>
#include <qt4/QtCore/qnamespace.h>


TestDrawer::TestDrawer(QWidget * parent)
    : QGLWidget(parent), scale_(1.0), map_file_num_(10)
{
    createPolygon();
    
    setMouseTracking(true);
    setFocusPolicy(Qt::TabFocus);

}

void TestDrawer::createPolygon()
{
    char file[20];
    sprintf(file, "../input_data/%i.map", map_file_num_);
    std::ifstream in(file);
    polygon_data_t polygon_data;
    in >> polygon_data;

    for(size_t i = 0; i < polygon_data.size(); ++i)
    {
        for(size_t j = 0; j < polygon_data[i].size(); ++j)
        {
            if(j > 0)
            {
                line_t l(polygon_data[i][j], polygon_data[i][j - 1]);
                if(l.is_horizontal())
                    std::cout << "is horizontal: " << "i = " << i << "; j = " << j << std::endl;
                if(l.is_vertical())
                    std::cout << "is vertical: " << "i = " << i << "; j = " << j << std::endl;
            }
        }
    }

    point_t top_left = top_left_corner(polygon_data);
    point_t bottom_right = bottom_right_corner(polygon_data);
    std::cout << top_left << " " << bottom_right << std::endl;
    points_ = random_points(top_left, bottom_right);

    polygon_ = get_polygon(polygon_data);
    points_states_.clear();
    for(Points::iterator i = points_.begin(); i != points_.end(); ++i) {
        points_states_.push_back(belongs((*i), polygon_));
    }

    updateGrid(800);

    h_position_ = -top_left.x;
    v_position_ = -top_left.y;
}

void TestDrawer::save(std::string const & file) const
{
    std::cout << "save" << std::endl;
    std::ofstream out(file.c_str());
    out << (polygon_);
    for(size_t i = 0; i < points_.size(); ++i)
    {
        out << points_[i] << " " << points_states_[i] << std::endl;
    }
    out.clear();
}

void TestDrawer::initializeGL()
{
    glDisable(GL_TEXTURE_2D);
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_COLOR_MATERIAL);
    glEnable(GL_BLEND);
    glEnable(GL_POLYGON_SMOOTH);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glClearColor(0, 0, 0, 0);
}

void TestDrawer::resizeGL(int w, int h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0, w, 0, h);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void TestDrawer::paintGL()
{
    glColor3f(0, 0, 0.4);
    glClearColor(0.8, 0.8, 0.8, 0);
    glClear(GL_COLOR_BUFFER_BIT);
    paintPolygon();
    paintPoints();
    paintGrid();
}

void TestDrawer::mousePressEvent(QMouseEvent * event)
{
    point_t point(event->x(), this->height() - event->y());
    std::cout << point << std::endl;
}

void TestDrawer::mouseMoveEvent(QMouseEvent *event)
{
    //printf("%d, %d\n", event->x(), event->y());
}

void TestDrawer::keyPressEvent(QKeyEvent* event)
{
    bool flag = true;
    switch (event->key())
    {
    case Qt::Key_Left:
        h_position_ -= 10;
        break;
    case Qt::Key_Right:
        h_position_ += 10;
        break;
    case Qt::Key_Up:
        v_position_ += 10;
        break;
   case Qt::Key_Down:
        v_position_ -= 10;
        break;
    case Qt::Key_Equal:
        scale_ *= 1.1;
        break;
    case Qt::Key_Minus:
        scale_ /= 1.1;
        break;
    case Qt::Key_S:
        char file[9];
        sprintf(file, "output%i", map_file_num_);
        save(file);
        break;
    case Qt::Key_PageUp:
        map_file_num_ = (map_file_num_ == 1) ? 10: (map_file_num_ - 1);
	if(map_file_num_ == 3 || map_file_num_ == 6)
		--map_file_num_;
        createPolygon();
        break;
    case Qt::Key_PageDown:
        map_file_num_ = (map_file_num_ == 10) ? 1: (map_file_num_ + 1);
	if(map_file_num_ == 3 || map_file_num_ == 6)
		++map_file_num_;
        createPolygon();
        break;
    case Qt::Key_1:
        updateGrid(grid_.cell_size() / 1.5);
        break;
    case Qt::Key_2:
        updateGrid(grid_.cell_size() * 1.5);
        break;
    default:
        event->ignore();
        flag = false;
        break;
    }
    if(flag) repaint();
}

void TestDrawer::paintPolygon()
{
    glLineWidth(2);
    for(size_t n = 0; n != polygon_.size(); ++n)
    {
        contour_t contour = polygon_[n];
        if(n > 0) glColor3f(0.5, 0.5, 0.5);
        for (size_t i = 0; i != contour.size(); ++i)
        {
            point_t point = scalePoint(contour[i]);
            glBegin(GL_LINE);
            glVertex2f(point.x, point.y);
            if(i != contour.size() - 1)
                point = scalePoint(contour[i + 1]);
            else
                point = scalePoint(contour[0]);
            glVertex2f(point.x, point.y);
            glEnd();
        }
    }
}

void TestDrawer::paintPoints()
{
    glPointSize(2);
    glBegin(GL_POINTS);
    Points::iterator i = points_.begin();
    int error_counter = 0;
    for(size_t j = 0; i != points_.end(); ++i, ++j) {
        point_t point = scalePoint(*i);
        if (!points_states_grid_[j]) {
            glColor3f(0.7, .0, .0);
        } else {
            glColor3f(.0, 0.7, .0);
        }
        if(points_states_grid_[j] != points_states_[j])
        {
            ++error_counter;
            glColor3f(.0, .0, 0.7);
        }
        glVertex2f(point.x, point.y);
    }
    glEnd();

    /*glPointSize(2);
    glBegin(GL_POINTS);
    Points::iterator i = points_.begin();
    for(size_t j = 0; i != points_.end(); ++i, ++j) {
        point_t point = scalePoint(*i);
        if (!points_states_[j]) {
            glColor3f(0.7, .0, .0);
        } else {
            glColor3f(.0, 0.7, .0);
        }
        glVertex2f(point.x, point.y);
    }
    glEnd();*/
}

point_t TestDrawer::scalePoint(point_t point) const
{
    point.x = (point.x + h_position_) * scale_;
    point.y = (point.y + v_position_) * scale_;
    return point;
}

void TestDrawer::paintGrid()
{
    if(grid_.cell_size() > 0)
    {

        glEnable(GL_ALPHA_TEST);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        //grid_t grid = data_->grid();
        for(size_t i = 0; i < grid_.size(); ++i)
        {
        //if(!grid[i].empty())
        //{
            std::vector<point_t> points;
            points.push_back(scalePoint(point_t(grid_[i].center().x - grid_.cell_size()/2, grid_[i].center().y - grid_.cell_size()/2)));
            points.push_back(scalePoint(point_t(grid_[i].center().x - grid_.cell_size()/2, grid_[i].center().y + grid_.cell_size()/2)));
            points.push_back(scalePoint(point_t(grid_[i].center().x + grid_.cell_size()/2, grid_[i].center().y + grid_.cell_size()/2)));
            points.push_back(scalePoint(point_t(grid_[i].center().x + grid_.cell_size()/2, grid_[i].center().y - grid_.cell_size()/2)));

            glColor3f(0.7, .2, .0);
            glPointSize(7);
            point_t center = scalePoint(grid_[i].center());
            glBegin(GL_POINT);
            glVertex2d(center.x, center.y);
            glEnd();

            if(grid_[i].center_in())
                glColor4d(0.0, 0.5, 0.0, 0.2);
            else
                glColor4d(0.0, 0.5, 0.0, 0);
            glBegin(GL_POLYGON);
            glVertex2d(points[0].x, points[0].y);
            glVertex2d(points[1].x, points[1].y);
            glVertex2d(points[2].x, points[2].y);
            glVertex2d(points[3].x, points[3].y);
            glEnd();

            glColor4d(0.0, 0.5, 0.0, 0.7);
            glBegin(GL_LINE_LOOP);
            glVertex2d(points[0].x, points[0].y);
            glVertex2d(points[1].x, points[1].y);
            glVertex2d(points[2].x, points[2].y);
            glVertex2d(points[3].x, points[3].y);
            glEnd();
        //}
        }

        glDisable(GL_BLEND);
        glDisable(GL_ALPHA_TEST);
    }
}

/*void TestDrawer::gridScale()
{
    std::cout << "c - pressed" << std::endl;
        scale(grid_, 500);
        polygon_data_t polygon_data(1);
        //polygon_data.push_back(std::vector<point_t>());
        for(size_t i = 0; i < grid_.size(); ++i)
        {
            for(size_t j = 0; j < grid_[i].size(); ++j)
            {
                if(polygon_data[0].size() == 0)
                {
                    polygon_data[0].push_back(grid_[i][j].first);
                }
                else
                {
                    if( std::find(polygon_data[0].begin(), polygon_data[0].end(), grid_[i][j].first)
                            == polygon_data[0].end() )
                    {
                         polygon_data[0].push_back(grid_[i][j].first);
                    }
                }
            }
        }
        polygon_ = get_polygon(polygon_data);
        std::cout << "grid scaled" << std::endl;
}*/

void TestDrawer::updateGrid(double cell_size)
{
    grid_ = grid_t(polygon_, cell_size);

    points_states_grid_.clear();
    int j = 0;
    int error_counter = 0;
    for(Points::iterator i = points_.begin(); i != points_.end(); ++i, ++j) {
        points_states_grid_.push_back(grid_.inside(*i));
        if(points_states_[j] != points_states_grid_[j])
        {
            ++error_counter;
        }
    }
    std::cout << "error_counter = " << error_counter << std::endl;
}
